
#include <stdio.h>
#include <sys/time.h>
#include <limits.h>

#include "timer.h"

/**********************************************************************************************\
* Functions that work with struct timeval                                                      *
\**********************************************************************************************/

/* Normalizes the timeval components */
struct timeval normalizeTimeval(struct timeval a)
{
	struct timeval out;
	out.tv_sec = a.tv_sec + (a.tv_usec / 1000000L);
	out.tv_usec = a.tv_usec % 1000000L;

	if (out.tv_usec < 0)
	{
		out.tv_usec += 1000000L;
		out.tv_sec--;
	}
	
	return out;
}

/* Adds one timeval to another */
struct timeval addTimeval(struct timeval a, struct timeval b)
{
  struct timeval out;
  out.tv_sec = a.tv_sec + b.tv_sec;
  out.tv_usec = a.tv_usec + b.tv_usec;
  return normalizeTimeval(out);
}

/* Subtracts one timeval from another */
struct timeval subtractTimeval(struct timeval a, struct timeval b)
{
  struct timeval out;
  out.tv_sec = a.tv_sec - b.tv_sec;
  out.tv_usec = a.tv_usec - b.tv_usec;
  return normalizeTimeval(out);
}

/* Gives a string representing the time spent */
const char* formatTimeval(struct timeval a)
{
	static char buf[100];
	g_snprintf(buf, sizeof(buf),
		"%li.%6.6lis", a.tv_sec, a.tv_usec);
	return buf;
}

/**********************************************************************************************\
* Functions that deal directly with the REAL timers                                            *
\**********************************************************************************************/

/* Local variables that hold timer information */
static struct itimerval timerReal;
static struct itimerval timerProfile;
static struct itimerval timerVirtual;

static int timerInit = 0;
static struct itimerval timerInitial;
static struct itimerval timerFinal;

/* Initializes the initial & final times */
static void initTimers()
{
	timerInitial.it_interval.tv_sec  = 0;
	timerInitial.it_interval.tv_usec = 0;
	
	timerInitial.it_value.tv_sec  = 60*60*24*30; /* 1 month */
	timerInitial.it_value.tv_usec = 0;
	
	
	timerFinal.it_interval.tv_sec = 0;
	timerFinal.it_interval.tv_usec = 0;
	
	timerFinal.it_value.tv_sec  = 0;
	timerFinal.it_value.tv_usec = 0;
	
	timerInit = 1;
}

/* Turn on all the timers */
static void startTimers()
{
	if (!timerInit) initTimers();
	
	setitimer(ITIMER_REAL,    &timerInitial, 0);
	setitimer(ITIMER_PROF,    &timerInitial, 0);
	setitimer(ITIMER_VIRTUAL, &timerInitial, 0);
}

/* Stops all the timers */
static void stopTimers()
{
	setitimer(ITIMER_REAL,    &timerFinal, &timerReal);
	setitimer(ITIMER_PROF,    &timerFinal, &timerProfile);
	setitimer(ITIMER_VIRTUAL, &timerFinal, &timerVirtual);
}

/* Keeps the timer going, but grabs the elapsed time interval */
static void restartTimers()
{
	setitimer(ITIMER_REAL,    &timerInitial, &timerReal);
	setitimer(ITIMER_PROF,    &timerInitial, &timerProfile);
	setitimer(ITIMER_VIRTUAL, &timerInitial, &timerVirtual);
}

/**********************************************************************************************\
* Functions that implement the management for user timers                                      *
\**********************************************************************************************/

static fpc_timer_t* topTimer = (fpc_timer_t*)0;

/* Pushes elapsed time onto the user timers */
static void heartbeatTimers()
{
	fpc_timer_t* scan;
	
	if (topTimer) restartTimers(); /* if there are things to update, beat the timer */
	
	for (scan = topTimer; scan; scan = scan->next)
	{
		scan->real    = 
			addTimeval(scan->real, 
				subtractTimeval(timerInitial.it_value, 
						timerReal.it_value));
		scan->profile = 
			addTimeval(scan->profile, 
				subtractTimeval(timerInitial.it_value, 
						timerProfile.it_value));
		scan->virtual = 
			addTimeval(scan->virtual, 
				subtractTimeval(timerInitial.it_value, 
						timerVirtual.it_value));
	}
}

/**********************************************************************************************\
* Functions that give access to user timers                                                    *
\**********************************************************************************************/

/* Prepare a new timer */
void initTimer(fpc_timer_t* x)
{
	x->running = 0;
	x->next = (fpc_timer_t*)0;

	x->real.tv_sec  = 0;
	x->real.tv_usec = 0;

	x->profile = x->virtual = x->real;
}

/* Zeros a user timer */
void zeroTimer(fpc_timer_t* x)
{
	if (x->running) heartbeatTimers();
	
	x->real.tv_sec  = 0;
	x->real.tv_usec = 0;
	x->profile = x->virtual = x->real;
}

/* Startup a user timer */
int startTimer(fpc_timer_t* x)
{
	if (x->running) return -1;
	
	heartbeatTimers(); /* Pulse the timers */
	
	x->next = topTimer;
	topTimer = x;
	x->running = 1;
	
	if (!x->next) startTimers();
	
	return 0;
}

/* Stop a user timer */
int stopTimer(fpc_timer_t* x)
{
	if (!x->running) return -1;
	
	heartbeatTimers(); /* Pulse the timers */
	
	if (topTimer == x)
	{
		topTimer = x->next;
	}
	else
	{
		fpc_timer_t* scan;
		for (scan = topTimer; scan->next != x; scan = scan->next);
		scan->next = x->next;
	}

	x->running = 0;
	
	if (!topTimer) stopTimers(); /* don't count down if no timers active */
	
	return 0;
}

/* Return the various timers */
struct timeval getRealTime(const fpc_timer_t* x)
{
	heartbeatTimers();
	return x->real;
}

struct timeval getProfileTime(const fpc_timer_t* x)
{
	heartbeatTimers();
	return x->profile;
}

struct timeval getVirtualTime(const fpc_timer_t* x)
{
	heartbeatTimers();
	return x->virtual;
}

